﻿using System;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MugenMvvmToolkit.Interfaces;
using MugenMvvmToolkit.Ioc;
using MugenMvvmToolkit.ViewModels;
using Should;

namespace MugenMvvmToolkitTest.Ioc
{
    [TestClass]
    public abstract class IocAdapterBaseTest<T> where T : IIocAdapter
    {
        #region Fields

        protected readonly Func<T> GetIocAdapter;

        #endregion

        #region Constructors

        protected IocAdapterBaseTest(Func<T> getIocAdapter)
        {
            GetIocAdapter = getIocAdapter;
        }

        #endregion

        #region Test methods

        [TestMethod]
        public virtual void TestCreateChildAndGetRoot()
        {
            using (IIocAdapter iocAdapter = GetIocAdapter())
            {
                iocAdapter.ShouldEqual(iocAdapter.GetRoot());

                IIocAdapter child = iocAdapter.CreateChild();
                iocAdapter.ShouldNotEqual(child);
                child.Container.ShouldNotBeNull();
                child.GetRoot().ShouldEqual(iocAdapter);
            }
        }

        [TestMethod]
        public virtual void TestSelfBindable()
        {
            using (IIocAdapter iocAdapter = GetIocAdapter())
            {
                var o = iocAdapter.Get(typeof(Simple));
                o.ShouldNotBeNull();
                var simple = iocAdapter.Get<Simple>();
                simple.ShouldNotBeNull();
                simple.ShouldNotEqual(o);
            }
        }

        [TestMethod]
        public virtual void TestBindToType()
        {
            using (IIocAdapter iocAdapter = GetIocAdapter())
            {
                iocAdapter.Bind<ISimple, Simple>(DependencyLifecycle.SingleInstance);

                var simple = (ISimple)iocAdapter.Get(typeof(ISimple));
                simple.ShouldNotBeNull();
                iocAdapter.Get<ISimple>().ShouldEqual(simple);
                iocAdapter.Get(typeof(ISimple)).ShouldEqual(simple);
            }

            using (IIocAdapter iocAdapter = GetIocAdapter())
            {
                iocAdapter.Bind(typeof(ISimple), typeof(Simple), DependencyLifecycle.SingleInstance);

                var simple = (ISimple)iocAdapter.Get(typeof(ISimple));
                simple.ShouldNotBeNull();
                iocAdapter.Get<ISimple>().ShouldEqual(simple);
                iocAdapter.Get(typeof(ISimple)).ShouldEqual(simple);
            }

            using (IIocAdapter iocAdapter = GetIocAdapter())
            {
                iocAdapter.Bind<ISimple, Simple>(DependencyLifecycle.TransientScopeInstance);

                var simple = (ISimple)iocAdapter.Get(typeof(ISimple));
                simple.ShouldNotBeNull();
                iocAdapter.Get<ISimple>().ShouldNotEqual(simple);
                iocAdapter.Get(typeof(ISimple)).ShouldNotEqual(simple);
            }

            using (IIocAdapter iocAdapter = GetIocAdapter())
            {
                iocAdapter.Bind(typeof(ISimple), typeof(Simple), DependencyLifecycle.TransientScopeInstance);

                var simple = (ISimple)iocAdapter.Get(typeof(ISimple));
                simple.ShouldNotBeNull();
                iocAdapter.Get<ISimple>().ShouldNotEqual(simple);
                iocAdapter.Get(typeof(ISimple)).ShouldNotEqual(simple);
            }
        }

        [TestMethod]
        public virtual void TestBindToConstant()
        {
            using (IIocAdapter iocAdapter = GetIocAdapter())
            {
                var constValue = new Simple();
                iocAdapter.BindToConstant<ISimple>(constValue);

                var simple = (ISimple)iocAdapter.Get(typeof(ISimple));
                simple.ShouldNotBeNull();
                simple.ShouldEqual(constValue);
                iocAdapter.Get<ISimple>().ShouldEqual(simple);
                iocAdapter.Get(typeof(ISimple)).ShouldEqual(simple);
            }

            using (IIocAdapter iocAdapter = GetIocAdapter())
            {
                var constValue = new Simple();
                iocAdapter.BindToConstant(typeof(ISimple), constValue);

                var simple = (ISimple)iocAdapter.Get(typeof(ISimple));
                simple.ShouldNotBeNull();
                simple.ShouldEqual(constValue);
                iocAdapter.Get<ISimple>().ShouldEqual(simple);
                iocAdapter.Get(typeof(ISimple)).ShouldEqual(simple);
            }
        }

        [TestMethod]
        public virtual void TestBindToTypeNamed()
        {
            const string named = "named";

            using (IIocAdapter iocAdapter = GetIocAdapter())
            {
                iocAdapter.Bind<ISimple, Simple>(DependencyLifecycle.SingleInstance, named);

                var simple = (ISimple)iocAdapter.Get(typeof(ISimple), named);
                simple.ShouldNotBeNull();
                iocAdapter.Get<ISimple>(named).ShouldEqual(simple);
                iocAdapter.Get(typeof(ISimple), named).ShouldEqual(simple);

                Action action = () => iocAdapter.Get<ISimple>();
                action.ShouldThrow();
            }

            using (IIocAdapter iocAdapter = GetIocAdapter())
            {
                iocAdapter.Bind(typeof(ISimple), typeof(Simple), DependencyLifecycle.SingleInstance, named);

                var simple = (ISimple)iocAdapter.Get(typeof(ISimple), named);
                simple.ShouldNotBeNull();
                iocAdapter.Get<ISimple>(named).ShouldEqual(simple);
                iocAdapter.Get(typeof(ISimple), named).ShouldEqual(simple);

                Action action = () => iocAdapter.Get<ISimple>();
                action.ShouldThrow();
            }

            using (IIocAdapter iocAdapter = GetIocAdapter())
            {
                iocAdapter.Bind<ISimple, Simple>(DependencyLifecycle.TransientScopeInstance, named);

                var simple = (ISimple)iocAdapter.Get(typeof(ISimple), named);
                simple.ShouldNotBeNull();
                iocAdapter.Get<ISimple>(named).ShouldNotEqual(simple);
                iocAdapter.Get(typeof(ISimple), named).ShouldNotEqual(simple);

                Action action = () => iocAdapter.Get<ISimple>();
                action.ShouldThrow();
            }

            using (IIocAdapter iocAdapter = GetIocAdapter())
            {
                iocAdapter.Bind(typeof(ISimple), typeof(Simple), DependencyLifecycle.TransientScopeInstance, named);

                var simple = (ISimple)iocAdapter.Get(typeof(ISimple), named);
                simple.ShouldNotBeNull();
                iocAdapter.Get<ISimple>(named).ShouldNotEqual(simple);
                iocAdapter.Get(typeof(ISimple), named).ShouldNotEqual(simple);

                Action action = () => iocAdapter.Get<ISimple>();
                action.ShouldThrow();
            }
        }

        [TestMethod]
        public virtual void TestBindToConstantNamed()
        {
            const string named = "named";

            using (IIocAdapter iocAdapter = GetIocAdapter())
            {
                var constValue = new Simple();
                iocAdapter.BindToConstant<ISimple>(constValue, named);

                var simple = (ISimple)iocAdapter.Get(typeof(ISimple), named);
                simple.ShouldNotBeNull();
                simple.ShouldEqual(constValue);
                iocAdapter.Get<ISimple>(named).ShouldEqual(simple);
                iocAdapter.Get(typeof(ISimple), named).ShouldEqual(simple);

                Action action = () => iocAdapter.Get<ISimple>();
                action.ShouldThrow();
            }

            using (IIocAdapter iocAdapter = GetIocAdapter())
            {
                var constValue = new Simple();
                iocAdapter.BindToConstant(typeof(ISimple), constValue, named);

                var simple = (ISimple)iocAdapter.Get(typeof(ISimple), named);
                simple.ShouldNotBeNull();
                simple.ShouldEqual(constValue);
                iocAdapter.Get<ISimple>(named).ShouldEqual(simple);
                iocAdapter.Get(typeof(ISimple), named).ShouldEqual(simple);

                Action action = () => iocAdapter.Get<ISimple>();
                action.ShouldThrow();
            }
        }

        [TestMethod]
        public virtual void TestUnbind()
        {
            using (IIocAdapter iocAdapter = GetIocAdapter())
            {
                var constValue = new Simple();
                iocAdapter.BindToConstant<ISimple>(constValue);
                iocAdapter.Get<ISimple>().ShouldEqual(constValue);

                iocAdapter.Unbind(typeof(ISimple));
                Action action = () => iocAdapter.Get<ISimple>().ShouldEqual(constValue);
                action.ShouldThrow();
            }
        }

        #endregion
    }

    public interface ISimple
    {
    }

    public class Simple : ISimple
    {
        public Guid Guid = Guid.NewGuid();
    }

    public class IntoClass
    {
        public IntoClass(Simple simple)
        {
            Simple = simple;
        }

        public Simple Simple { get; set; }
    }

    public class IntoClass1
    {
        public IntoClass1(Simple simple)
        {
            Simple = simple;
        }

        public Simple Simple { get; set; }
    }

    public class GenericViewModel<T>
    {
    }
}